//#define PlayerCharacter__UseHashedPasswords

using System;
using System.Xml.Serialization;
using SharpMud.MudLib.SAMPLE.Environment;
using SharpMud.MudLib.SAMPLE.Entities;

#if Use_Common_MudLib
using SharpMud.MudLib.Common.Resourcing;
using SharpMud.MudLib.Common.Entities.Players;
using SharpMud.MudLib.Common.Environment;
using SharpMud.MudLib.Common.Environment.Persistance;
#endif

namespace SharpMud.MudLib.SAMPLE.Entities
{
	[XmlRoot("PlayerCharacter")]
	public class PlayerCharacter : IDisposable, SAMPLE.Entities.ICharacter
#if Use_Common_MudLib
            , Common.Entities.Players.IPlayerCharacter
#endif
	{
		#region Private Variables
		private string					_PlayerName;
        private Guid                    _ID;
#if PlayerCharacter__UseHashedPasswords
		private byte[]					_HashedPassword;
#else
		private string					_Password;
#endif
		private string					_EmailAddress;
		
#if Use_Common_MudLib
        private Common.Entities.Players.IPlayerManager _Host;
#else
        private PlayerMetabase			_HostPlayerMetabase;
#endif
		private bool					_EmailIsPrivate;
        private bool                    _TurnedOffCreation;
		private Net.TelnetPlayerSession _Session;
		#endregion

		#region Public Properties
		[XmlIgnore()]
		public bool IsInCreation
		{
			get
			{
				return (!_TurnedOffCreation);
			}
		}

		[XmlElement("PlayerName")]
		public string PlayerName
		{
			get
			{
				return _PlayerName;
			}
			set
			{
				if(IsInCreation)
					_PlayerName = value;
				else
					throw new InvalidOperationException();
			}
		}

#if PlayerCharacter__UseHashedPasswords
		[XmlElement("HashedPassword")]
		public byte[] HashedPassword
		{
			get
			{
				return _HashedPassword;
			}
			set
			{
				if(IsInCreation)
					_HashedPassword = value;
				else
					throw new InvalidOperationException();
			}
		}
#else
		[XmlElement("Password")]
		public string Password
		{
			get
			{
				return this._Password;
			}
			set
			{
				if(IsInCreation)
					this._Password = value;
				else
					throw new InvalidOperationException();
			}
		}
#endif

		[XmlElement("EmailAddress")]
		public string EmailAddress
		{
			get
			{
				return _EmailAddress;
			}
			set
			{
				_EmailAddress = value;
			}
		}

        [XmlElement("EmailIsPrivate")]
		public bool EmailIsPrivate
		{
			get
			{
				return _EmailIsPrivate;
			}
			set
			{
				_EmailIsPrivate = value;
			}
		}

        [XmlElement("ID")]
        public string IDtext
        {
            get
            {
                return _ID.ToString();
            }
            set
            {
                if (_TurnedOffCreation)
                    throw new InvalidOperationException();
                _ID = new Guid(value);
            }
        }

        [XmlIgnore()]
        public Guid ID
        {
            get
            {
                return _ID;
            }
            set
            {
                if (_TurnedOffCreation)
                    throw new InvalidOperationException();
                _ID = value;
            }
        }

#if Use_Common_MudLib
        [XmlIgnore()]
        public IPlayerManager Host
        {
            get
            {
                return _Host;
            }
        }

        [XmlIgnore()]
        public Common.IBaseMudLibRoot HostServer
        {
            get
            {
                return _Host.Host;
            }
        }
#else
		[XmlIgnore()]
		public PlayerMetabase			HostPlayerMetabase
		{
			get
			{
				return _HostPlayerMetabase;
			}
		}

		[XmlIgnore()]
		public MudLib.SAMPLE.MudLibRoot	HostServerApp
		{
			get
			{
				return _HostPlayerMetabase.HostServerApp;
			}
		}
		[XmlIgnore()]
		public PlayerSystem HostPlayerSystem
		{
			get
			{
				return _HostPlayerMetabase.HostPlayerSystem;
			}
		}
#endif
	
		[XmlIgnore()]
		public Net.TelnetPlayerSession Session
		{
			get
			{
				return _Session;
			}
			set
			{
				_Session = value;
			}
		}
		#endregion

		#region Constructors
		public PlayerCharacter()
		{
			_TurnedOffCreation = false;
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Instructs the player metabase to save this player
		/// </summary>
		public void Save()
		{
#if Use_Common_MudLib
            this.Host.SavePlayer(this);
#else
            this._HostPlayerMetabase.SavePlayer(this.PlayerName);
#endif
		}

		/// <summary>
		/// Quits the character by disconnecting its session and removing it from the game world
		/// </summary>
		public void Quit()
		{
			lock(this.Session)
			{
				this.Session.Write("^!^!^![blue]Bye now! [white]Come back soon!^!^!");
				this.Session.Flush();
				this.Session.SessionState.Clear();
				this.Session.SessionState.Push(new Net.SessionStates.Quit(),true);
			}
		}

		/// <summary>
		/// Turns off 'creation' for this character. That is, several properties become read-only.
		/// </summary>
		public void TurnOffCreation()
		{
            if (this._ID == null || this._ID == Guid.Empty)
                this._ID = Guid.NewGuid();
			this._TurnedOffCreation=true;
		}
		#endregion

		#region Password Tracking and Hashing

		/// <summary>
		/// Determines if the player has the given password
		/// </summary>
		/// <param name="passwordGuess">The guess at what this player's password is</param>
		/// <returns>
		/// If the player's password is corrupt,
		/// missing, or otherwise unusable, TriState.UseDefault is returned. Otherwise, TriState.True
		/// is returned on successful match, and TriState.False is returned on a mismatch.</returns>
		public TriState HasPassword(string passwordGuess)
		{
			bool b = false;
#if PlayerCharacter__UseHashedPasswords
			if(this._HashedPassword==null)
				return TriState.UseDefault;
			if(this._HashedPassword.Length==0)
				return TriState.UseDefault;
			
			b = HasHashedPassword(DefaultTextEncoding.GetString(PerformPasswordHash(passwordGuess)));
			if(b)
				return TriState.True;
			else
				return TriState.False;
#else
			if(this._Password==null)
				return TriState.UseDefault;
			if(this._Password==String.Empty)
				return TriState.UseDefault;

			b = (passwordGuess == this._Password);
			if(b)
				return TriState.True;
			else
				return TriState.False;
#endif
		}
#if PlayerCharacter__UseHashedPasswords
		private bool HasHashedPassword(string hashedPasswordGuess)
		{
			if(DefaultTextEncoding.GetString(this._HashedPassword) == hashedPasswordGuess)
				return true;
			else
				return false;
		}
#endif
		internal void SetPassword(string newPassword)
		{
#if PlayerCharacter__UseHashedPasswords
			this._HashedPassword = PerformPasswordHash(newPassword);
#else
			this._Password = newPassword;
#endif
		}
		/// <summary>
		/// Sets the players password to the new password, if the password guess for the old password
		/// is correct.
		/// </summary>
		/// <param name="oldPasswordGuess">A guess at the player's old password</param>
		/// <param name="newPassword">The new password for the player</param>
		/// <exception cref="PasswordMismatchException">Thrown when the password guess is definitely not
		/// the player's password.</exception>
		public void SetPassword(string oldPasswordGuess, string newPassword)
		{
			if(HasPassword(oldPasswordGuess)==TriState.False)
			{
				throw new PasswordMismatchException(this.PlayerName);
			}
			this.SetPassword(newPassword);
		}
#if PlayerCharacter__UseHashedPasswords
		private byte[] PerformPasswordHash(string input)
		{
			return DefaultTextEncoding.GetBytes(PlayerCharacter.HashString(DefaultPasswordHasher,input));
		}
#endif
		#endregion

		#region IDisposable Members
		/// <summary>
		/// Shuts-down a player and de-registers itself from the mud world
		/// </summary>
		public void Dispose()
		{
			// TODO:  Add PlayerCharacter.Dispose implementation
		}

		#endregion

		#region Initialize()
		/// <summary>
		/// Initializes the Player (does not register into the game world!)
		/// </summary>
#if Use_Common_MudLib
        public void Initialize(Common.Entities.Players.IPlayerManager playerManager)
#else
        public void Initialize(PlayerMetabase hostPlayerMetabase)
#endif
		{
            if (this._ID == null || this._ID == Guid.Empty)
                this._ID = Guid.NewGuid();
#if Use_Common_MudLib
            this.Location = new RoomReference(((IRoom)this.HostServer.EnvironmentManager.GetResource(this.Location.RoomGuid)));
            this._Host = playerManager;
#else
            _HostPlayerMetabase = hostPlayerMetabase;
			this.Location.Room = this.HostServerApp.EnvironmentSystem.GetRoom(this.Location);
            
#endif
		}
		#endregion

		#region Static Public Properties/Methods
#if PlayerCharacter__UseHashedPasswords
		public System.Security.Cryptography.HashAlgorithm DefaultPasswordHasher
		{
			get
			{
				return new System.Security.Cryptography.SHA256Managed();
			}
		}
		public static string HashString(System.Security.Cryptography.HashAlgorithm hasher, string input)
		{
			return HashString(hasher,input,DefaultTextEncoding);
		}
		public static string HashString(System.Security.Cryptography.HashAlgorithm hasher, string input, System.Text.Encoding textEncoding)
		{
			return textEncoding.GetString(hasher.ComputeHash(textEncoding.GetBytes(input)));
		}
#endif
		private static System.Text.Encoding DefaultTextEncoding
		{
			get
			{
				return System.Text.Encoding.Default;
			}
		}
		public static bool UsesHashedPasswords
		{
			get
			{
#if PlayerCharacter__UseHashedPasswords
				return true;
#else
				return false;
#endif
			}
		}
		#endregion

		#region ICharacter Members
		string ICharacter.Name
		{
			get
			{
				return this.PlayerName;
			}
		}
//		Net.TelnetPlayerSession Session
//		{
//			get
//			{
//				return _Session;
//			}
//		}
		[XmlElement("Location")]
#if Use_Common_MudLib
        private Common.Environment.Persistance.RoomReference _Location;
        public Common.Environment.Persistance.RoomReference Location
#else
        private Environment.RoomID		_Location;
		public Environment.RoomID Location
#endif
        {
			get
			{
				return _Location;
			}
			set
			{
				_Location = value;
			}
		}

		#endregion

		#region IDescribable Members

#if Use_Common_MudLib
		string Common.Environment.IDescribable.GetShortDescription(SharpMud.MudLib.Common.Entities.ICharacter looker)
#else
        string SAMPLE.Environment.IDescribable.GetShortDescription(SharpMud.MudLib.SAMPLE.Entities.ICharacter looker)
#endif
        {
			return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(this.PlayerName);
		}

#if Use_Common_MudLib
		string Common.Environment.IDescribable.GetDeclarativeShortDescription(SharpMud.MudLib.Common.Entities.ICharacter looker)
#else
        string SAMPLE.Environment.IDescribable.GetDeclarativeShortDescription(SharpMud.MudLib.SAMPLE.Entities.ICharacter looker)
#endif
		{
			System.Text.StringBuilder sb = new System.Text.StringBuilder(
				((IDescribable)this).GetShortDescription(looker).Trim()
				);
			//TODO: Make this more dynamic. IE " is standing here" versus " is resting here", etc etc
			sb.Append(" is here");
			return sb.ToString();
		}

#if Use_Common_MudLib
		string Common.Environment.IDescribable.GetIntegralShortDescription(SharpMud.MudLib.Common.Entities.ICharacter looker)
#else
        string SAMPLE.Environment.IDescribable.GetIntegralShortDescription(SharpMud.MudLib.SAMPLE.Entities.ICharacter looker)
#endif
		{
			return String.Empty;
		}

#if Use_Common_MudLib
		string Common.Environment.IDescribable.GetLongDescription(SharpMud.MudLib.Common.Entities.ICharacter looker)
#else
        string SAMPLE.Environment.IDescribable.GetLongDescription(SharpMud.MudLib.SAMPLE.Entities.ICharacter looker)
#endif
		{
			//return this.LongDescription;
			return String.Empty;
		}

		#endregion

#if Use_Common_MudLib

        #region ICharacter Members

        SharpMud.MudLib.Common.Environment.IRoom SharpMud.MudLib.Common.Entities.ICharacter.ParentRoom
        {
            get {
                return (this._Location.Room as SharpMud.MudLib.Common.Environment.IRoom);
            }
        }

        #endregion

        #region IHasInstanceGuid Members

        IGuidIdentifiedResourceHost SharpMud.MudLib.Common.Resourcing.IHasInstanceGuid.Host
        {
            get {
                return (IGuidIdentifiedResourceHost)HostPlayerMetabase;
            }
        }

        Guid SharpMud.MudLib.Common.Resourcing.IHasInstanceGuid.ID
        {
            get { return this._ID; }
        }

        #endregion

#endif


        #if Use_Common_MudLib
        #region IPlayerCharacter Members
        
        string SharpMud.MudLib.Common.Entities.Players.IPlayerCharacter.PlayerName
        {
            get { return this._PlayerName; }
        }

        #endregion

        #region IUser Members

        SharpMud.Server.Systems.Security.Entities.UserReference SharpMud.Server.Systems.Security.Entities.IUser.GetReference()
        {
            return new SharpMud.Server.Systems.Security.Entities.UserReference(this);
        }

        bool SharpMud.Server.Systems.Security.Entities.IUser.HasPassword(string passwordGuess)
        {

            TriState v = this.HasPassword(passwordGuess);
            if (v == TriState.True)
                return true;
            else if (v == TriState.False)
                return false;
            else
                throw new NotImplementedException();
        }

        bool SharpMud.Server.Systems.Security.Entities.IUser.IsInRole(SharpMud.Server.Systems.Security.Entities.UserRoleReference roleReference)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        bool SharpMud.Server.Systems.Security.Entities.IUser.IsInRole(SharpMud.Server.Systems.Security.Entities.IUserRole role)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        bool SharpMud.Server.Systems.Security.Entities.IUser.IsInRole(string roleName)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion

        #region IEntity Members

        SharpMud.Server.Systems.Security.Entities.EntityReference SharpMud.Server.Systems.Security.Entities.IEntity.GetReference()
        {
            return new SharpMud.Server.Systems.Security.Entities.EntityReference(this);
        }

        Guid SharpMud.Server.Systems.Security.Entities.IEntity.ID
        {
            get { return this._ID; }
        }

        string SharpMud.Server.Systems.Security.Entities.IEntity.Name
        {
            get { return this.Player; }
        }

        #endregion

        #region IActMessageReceiver Members

        void SharpMud.MudLib.Common.Interaction.Messaging.Act.IActMessageReceiver.SendMessage(SharpMud.MudLib.Common.Interaction.Messaging.Act.IActMessage message)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion

        #region IHasInstanceUri Members

        IUriIdentifiedResourceHost IHasInstanceUri.Host
        {
            get { return (IUriIdentifiedResourceHost)this.Host; }
        }

        Uri IHasInstanceUri.ID
        {
            get { return new Uri(Host.BaseUri, "/" + this.PlayerName.Trim().ToLower()); }
        }

        #endregion
        #endif

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }
}
